package com.myk.game.gobangchess.rooms;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.myk.game.gobangchess.CustomBottom;
import com.myk.game.gobangchess.Mode;
import com.myk.game.gobangchess.Player;
import com.myk.game.gobangchess.Sign;
import com.myk.game.gobangchess.constants.PathCost;
import com.myk.game.gobangchess.constants.WinMode;
import com.myk.game.gobangchess.rooms.absrooms.AbsMultiViewChessRoom;
import com.myk.game.gobangchess.signs.CacheSign;
import com.myk.game.gobangchess.signs.ColorSign;
import com.myk.game.gobangchess.signs.GroundSign;
import com.myk.game.gobangchess.utils.FileUtils;
import com.myk.game.gobangchess.utils.GridsUtils;
import com.myk.game.gobangchess.utils.Pair;

import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 卡拉德象棋
 */
public class KaLaDeRoom extends AbsMultiViewChessRoom {
    private static final Lock statisticsLK = new ReentrantLock();
    String roomDataPath = PathCost.ROOM_DATA_DIR + KaLaDeRoom.class.getSimpleName() + "/";
    String gridCacheFile = roomDataPath + "statistics.txt";

    boolean modeSelected = false; // 判断是否完成模式选择

    boolean humanStart; // 是否是自由布阵
    int humanStartFlag; // 双方是否是都想要自由布阵 0 -> 没有 1 -> 黑 2 -> 白 3 -> 都
    List<Pair<String, Boolean>> historyInfo = new ArrayList<>(); // 历史局面
    List<Chess> chessList = new ArrayList<>(); // 所有棋子，用来统计数据

    private CacheSign cacheSign; //缓存标记

    private static final String MODE_120SEC = "步时120s";
    private static final String MODE_100SEC = "步时100s";
    private static final String MODE_90SEC = "步时90s";
    private static final String MODE_80SEC = "步时80s";
    private static final String MODE_60SEC = "步时60s";

    public KaLaDeRoom(String roomName) {
        super(roomName, "\t\t一款二人对弈的象棋类战争游戏，双方分别扮演敌对双方，在棋盘战场上进行厮杀，夺取最终胜利。", "卡拉德象棋", GridsUtils.createEmptyGrids(10, 10), true, true,MODE_60SEC,MODE_80SEC,MODE_90SEC,MODE_100SEC,MODE_120SEC);
        initChessBoardStyle();
    }

    @Override
    public int getCountdownSec() {
        if (Mode.isCurMode(this, MODE_60SEC)){
            return 60;
        }
        if (Mode.isCurMode(this, MODE_80SEC)){
            return 80;
        }
        if (Mode.isCurMode(this, MODE_90SEC)){
            return 90;
        }
        if (Mode.isCurMode(this, MODE_100SEC)){
            return 100;
        }
        if (Mode.isCurMode(this, MODE_120SEC)){
            return 120;
        }
        return super.getCountdownSec();
    }

    //创建initChessBoardStyle方法
    private void initChessBoardStyle(){
        List<Sign> chessboardStyle = new ArrayList<>();
        int row = curGrids.length;
        int col = curGrids[0].length;
        //绘制类似国际象棋的黑白相间的格子
        List<Integer> bGrids = new ArrayList<>();
        List<Integer> wGrids = new ArrayList<>();
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                int index = getIndex(i, j);
                if ((i + j) % 2 == 0) {
                    bGrids.add(index);
                }
                else {
                    wGrids.add(index);
                }
            }
        }
        chessboardStyle.add(new ColorSign(bGrids, "#FF888888"));
        chessboardStyle.add(new ColorSign(wGrids, "#FFDDDDDD"));
        //画边框
        chessboardStyle.add(new GroundSign(getIndex(0, 0), getIndex(3, 9), "#00000000", "#FFAA6622", 2.2f, 0, 0));
        chessboardStyle.add(new GroundSign(getIndex(6, 0), getIndex(9, 9), "#00000000", "#FFAA6622", 2.2f, 0, 0));
        //将背景封装为缓存标记
        cacheSign = new CacheSign(this,"background",1,true,chessboardStyle); //*** 修改背景样式记得改这里的版本号！！！
    }

    @Override
    public CacheSign getCacheSignByName(String cacheName) {
        return cacheSign;
    }

    @Override
    public String getGameInfo(int view) {
        //先清空棋盘
        curGrids = GridsUtils.copyGrids(originGrids);
        signList.clear();
        signList.add(cacheSign);
        return super.getGameInfo(view);
    }

    @Override
    public String getCoordMode() {
        return "41410000";
    }

    @Override
    protected void doStart() {
        super.doStart();
        if (player_black != null) {
            playerSelectMode(player_black);
        }
        if (player_white != null && player_white != player_black) {
            playerSelectMode(player_white);
        }
    }

    private void addStatistics(Map<String, Integer> data, String key, int cnt) {
        data.put(key, data.getOrDefault(key, 0) + cnt);
    }

    @Override
    public void doOver(int winMode, String winMsg) {
        updateStatisticsFile(winMode);
        super.doOver(winMode, winMsg);
    }

    private void updateStatisticsFile(int winMode) {
        statisticsLK.lock();
        try {
            if (checkSaveSafeTime() && (winMode == WinMode.BOTH_DRAW || winMode == WinMode.BLACK_WIN || winMode == WinMode.WHITE_WIN || winMode == WinMode.WHITE_ADMIT || winMode == WinMode.BLACK_ADMIT || winMode == WinMode.WHITE_LEAVE || winMode == WinMode.BLACK_LEAVE)) {
                // 加载数据
                Map<String, Integer> data = new HashMap<>();
                FileUtils.createOrExistsDir(roomDataPath);
                String cachedData = FileUtils.readFile2String(gridCacheFile);
                if (cachedData != null && !cachedData.isEmpty()) {
                    try {
                        Type type = new TypeToken<HashMap<String, Integer>>() {
                        }.getType();
                        data = new Gson().fromJson(cachedData, type);
                    }
                    catch (Exception e) {
                        System.out.println("卡象统计数据加载出错:" + e);
                    }
                }
                // 统计数据
                for (Chess c : chessList) {
                    KaLaDeChess kc = (KaLaDeChess) c;
                    // 统计移动数
                    addStatistics(data, kc.mName + " 移动数", kc.moveCount);
                    // 统计吃子数
                    addStatistics(data, kc.mName + " 吃子数", kc.eatCount);
                }
                for (Side side : sideMap.values()) {
                    for (Chess c : side.chessList) {
                        // 统计存活数
                        addStatistics(data, c.mName + " 存活数", 1);
                    }
                }
                if (winMode == WinMode.WHITE_WIN || winMode == WinMode.BLACK_ADMIT || winMode == WinMode.BLACK_LEAVE) {
                    // 先手获胜
                    addStatistics(data, "先手获胜数", 1);
                }
                addStatistics(data, "对局数", 1);
                FileUtils.writeFileFromString(gridCacheFile, new Gson().toJson(data));  //缓存到本地
            }
        }
        finally {
            statisticsLK.unlock();
        }
    }

    @Override
    public void resetGrids() {
        super.resetGrids();
        historyInfo.clear();
        modeSelected = false;
        chessList.clear();
        humanStartFlag = 0;
        humanStart = false;
        gameTip = "随机对称布阵模式";
    }

    /**
     * 让玩家选择游玩模式
     *
     * @param player0 给哪个玩家添加按钮
     */
    private void playerSelectMode(Player player0) {
        player0.registerCustomBottom(new CustomBottom(this, "自由暗布阵", player -> {
            if (player == player_black) {
                humanStartFlag |= 1;
                gameTip = "仅黑方选用自由暗布阵模式，仍采用随机对称布阵";
            }
            if (player == player_white) {
                humanStartFlag |= 2;
                gameTip = "仅白方选用自由暗布阵模式，仍采用随机对称布阵";
            }
            if (humanStartFlag == 3) {
                gameTip = "双方都是选用自由暗布阵模式";
                humanStart = true;
            }
            noticeAllRefreshGameInfo();//通知界面刷新
        }), new CustomBottom(this, "随机对称\n布阵", player -> {
            humanStart = false;
            if (player == player_black) {
                humanStartFlag &= 2;
                gameTip = "仅白方选用自由暗布阵模式，仍采用随机对称布阵";
            }
            if (player == player_white) {
                humanStartFlag &= 1;
                gameTip = "仅黑方选用自由暗布阵模式，仍采用随机对称布阵";
            }
            if (humanStartFlag == 0) {
                gameTip = "随机对称布阵模式";
            }
            noticeAllRefreshGameInfo();//通知界面刷新
        }), new CustomBottom(this, "开始对弈", player -> {
            if (player_black != null) {
                player_black.clearCustomBottom();
                player_black.dispatchCustomBottom();
                if (humanStart) {
                    sideMap.get(1).startSettingUp(this, player_black);
                }
                else {
                    sideMap.get(1).finishSettingUp(this, player_black);
                }
            }
            if (player_white != null) {
                if (player_white != player_black) {
                    player_white.clearCustomBottom();
                    player_white.dispatchCustomBottom();
                }
                if (humanStart) {
                    sideMap.get(2).startSettingUp(this, player_white);
                }
                else {
                    sideMap.get(2).finishSettingUp(this, player_white);
                }
            }
            modeSelected = true; // 设置为模式选择完毕
            initChess();
            if (humanStart) {
                noticeAllMove();
            }
            else {
                noticeFirstMove();
            }
        }));
    }

    @Override
    public void initSides() {
        sideMap.put(1, new KaLaDeSide(1));
        sideMap.put(2, new KaLaDeSide(2));
    }

    @Override
    public void noticeFirstMove() {
        noticeWhiteMove();
    }

    public void initChess() {
        Side side1 = sideMap.get(1);
        Side side2 = sideMap.get(2);
        // 生成兵
        List<Pair<Integer, Integer>> postions = new ArrayList<>(Arrays.asList(new Pair<>(2, 1), new Pair<>(2, 2), new Pair<>(3, 3), new Pair<>(3, 4), new Pair<>(2, 8), new Pair<>(2, 7), new Pair<>(3, 6), new Pair<>(3, 5)));
        for (Pair<Integer, Integer> pos : postions) {
            side1.chessList.add(new Pawn(1, pos.first, pos.second));
            Pair<Integer, Integer> pos2 = couterpartPair(pos);
            side2.chessList.add(new Pawn(2, pos2.first, pos2.second));
        }
        // 随机对称生成其他棋子

        // 复制自频道卡象赛的布局生成程序（用C++写的），按照java修改了语法
        postions= new ArrayList<>(Arrays.asList(new Pair<>(2,3),new Pair<>(1,1),new Pair<>(0,1),new Pair<>(0,2),new Pair<>(0,3),new Pair<>(0,4),
                new Pair<>(0,5),new Pair<>(0,6),new Pair<>(0,7),new Pair<>(0,8),new Pair<>(1,8),new Pair<>(2,6)));
        int[] board_pos ={-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
        int f=4, m_cnt=1,rand_tmp,pos_tmp;
        int m_pos=(int)Math.floor(Math.random()*6)+3;
        board_pos[m_pos]=0;
        if(Math.random()>0.5){
            for(;f<6;f++){
                rand_tmp=(int)Math.floor(Math.random()*(8-f))+1;
                pos_tmp=m_pos;
                for(;rand_tmp>0;rand_tmp--){
                    do{
                        pos_tmp++;
                        if(pos_tmp==m_pos+3) pos_tmp-=5;
                    }while(board_pos[pos_tmp]!=-1);
                }
                board_pos[pos_tmp]=f;
                if(pos_tmp!=1 && pos_tmp!=2 && pos_tmp!=11 &&pos_tmp!=0)
                    m_cnt++;
            }
        }
        for(int k=1;k<4;k++){
            rand_tmp=(int)Math.floor(Math.random()*(8-m_cnt))+1;
            pos_tmp=2;
            for(;rand_tmp>0;rand_tmp--){
                do{
                    pos_tmp++;
                    if(pos_tmp==9) pos_tmp=11;
                    if(pos_tmp==12) pos_tmp=0;
                    if(pos_tmp==1) pos_tmp=3;
                }while(board_pos[pos_tmp]!=-1);
            }
            board_pos[pos_tmp]=k;
        }
        for(;f<12;f++){
            rand_tmp=(int)Math.floor(Math.random()*(12-f))+1;
            pos_tmp=-1;
            for(;rand_tmp>0;rand_tmp--){
                do{
                    pos_tmp++;
                    if(pos_tmp==12) pos_tmp=0;
                }while(board_pos[pos_tmp]!=-1);
            }
            board_pos[pos_tmp]=f;
        }
        Iterator<Pair<Integer, Integer>> iter = postions.iterator();
        Pair<Integer, Integer> pos,pos2;
        for(int j=0;j<12;j++){
            pos=iter.next();
            pos2 = couterpartPair(pos);
            if(board_pos[j]==0){
                side1.chessList.add(new General(1, pos.first, pos.second));
                side2.chessList.add(new General(2, pos2.first, pos2.second));
            }else if(board_pos[j]==1){
                side1.chessList.add(new Dragon(1, pos.first, pos.second));
                side2.chessList.add(new Dragon(2, pos2.first, pos2.second));
            }else if(board_pos[j]==2 || board_pos[j]==3){
                side1.chessList.add(new Bishop(1, pos.first, pos.second));
                side2.chessList.add(new Bishop(2, pos2.first, pos2.second));
            }else if(board_pos[j]==4 || board_pos[j]==5){
                side1.chessList.add(new Guard(1, pos.first, pos.second));
                side2.chessList.add(new Guard(2, pos2.first, pos2.second));
            }else if(board_pos[j]==6 || board_pos[j]==7){
                side1.chessList.add(new Horse(1, pos.first, pos.second));
                side2.chessList.add(new Horse(2, pos2.first, pos2.second));
            }else if(board_pos[j]==8 || board_pos[j]==9){
                side1.chessList.add(new Cannon(1, pos.first, pos.second));
                side2.chessList.add(new Cannon(2, pos2.first, pos2.second));
            }else{
                side1.chessList.add(new Chariot(1, pos.first, pos.second));
                side2.chessList.add(new Chariot(2, pos2.first, pos2.second));
            }
        }

//        postions = new ArrayList<>(Arrays.asList(new Pair<>(1, 1), new Pair<>(1, 8), new Pair<>(2, 3), new Pair<>(2, 6), new Pair<>(0, 1), new Pair<>(0, 2), new Pair<>(0, 3), new Pair<>(0, 4), new Pair<>(0, 5), new Pair<>(0, 6), new Pair<>(0, 7), new Pair<>(0, 8)
//
//        ));
//        Collections.shuffle(postions);
//        Iterator<Pair<Integer, Integer>> iter = postions.iterator();
//
//        // 帅
//        Pair<Integer, Integer> pos = iter.next();
//        Pair<Integer, Integer> pos2 = couterpartPair(pos);
//        side1.chessList.add(new General(1, pos.first, pos.second));
//        side2.chessList.add(new General(2, pos2.first, pos2.second));
//        // 龙
//        pos = iter.next();
//        pos2 = couterpartPair(pos);
//        side1.chessList.add(new Dragon(1, pos.first, pos.second));
//        side2.chessList.add(new Dragon(2, pos2.first, pos2.second));
//        for (int i = 0; i < 2; i++) {
//            // 车
//            pos = iter.next();
//            pos2 = couterpartPair(pos);
//            side1.chessList.add(new Chariot(1, pos.first, pos.second));
//            side2.chessList.add(new Chariot(2, pos2.first, pos2.second));
//            // 马
//            pos = iter.next();
//            pos2 = couterpartPair(pos);
//            side1.chessList.add(new Horse(1, pos.first, pos.second));
//            side2.chessList.add(new Horse(2, pos2.first, pos2.second));
//            // 炮
//            pos = iter.next();
//            pos2 = couterpartPair(pos);
//            side1.chessList.add(new Cannon(1, pos.first, pos.second));
//            side2.chessList.add(new Cannon(2, pos2.first, pos2.second));
//            // 象
//            pos = iter.next();
//            pos2 = couterpartPair(pos);
//            side1.chessList.add(new Bishop(1, pos.first, pos.second));
//            side2.chessList.add(new Bishop(2, pos2.first, pos2.second));
//            // 士
//            pos = iter.next();
//            pos2 = couterpartPair(pos);
//            side1.chessList.add(new Guard(1, pos.first, pos.second));
//            side2.chessList.add(new Guard(2, pos2.first, pos2.second));
//        }

        for (Side side : sideMap.values()) {
            chessList.addAll(side.chessList);
        }
    }

    private Pair<Integer, Integer> couterpartPair(Pair<Integer, Integer> pair0) {
        return new Pair<>(curGrids.length - 1 - pair0.first, pair0.second);
    }

    @Override
    public String getRuleText() {
        return "棋盘：靠近本方的4行为本阵，靠近对方的4行为敌阵，中间2行为前线\n" +
                "棋子（无特殊说明时，走法与吃法相同）\n" +
                "帅：横竖相邻1格；不能进入敌阵；帅与帅不能横竖直接照面，也不能进入以对方帅为中心的5x5格内\n" +
                "车：横竖不限格，不越子\n" +
                "马：走“日”字，无蹩腿\n" +
                "士：斜线相邻1格\n" +
                "象：斜线不限格但唯独不能到相邻1格。相当于国际象棋的象删去士的走法\n" +
                "龙：1、走子时，横竖不限格或斜线相邻1格；向前行走时可以越过不超过1个棋子，其余不越子。2、吃子时，横竖相邻1或2格，或斜线相邻1格，不越子\n" +
                "炮：1、走子时，横竖不限格，不越子，与车相同。2、吃子时，横竖跳过1个棋子，或横竖跳过相邻1格（该格内有无棋子均可），相当于中国象棋的炮+帖木儿象棋的洞屋\n" +
                "兵：1、在本阵，正前/左前/右前1格，但左前/右前只能行走不能吃子。2、在前线，正前1格。3，在敌阵，正前/左前/右前/正后1格\n" +
                " " +
                "对局开始：有2种模式\n" +
                "1、系统随机生成一个双方轴对称的初始布阵\n" +
                "2、自由暗布阵。双方各8个兵固定，在剩下各12格内放置其它棋子，顺序为：白方公开帅位、黑方公开帅位、双方各暗自布好剩余11枚棋子、双方同时公开全部子力位置开始对局\n" +
                "无论何种模式，开始对局后白方第1步均不能吃子\n" +
                " " +
                "对局结束：将死或困毙对方帅为胜。若形成3次循环局面，单方面长将判负，其余判和;\n";
    }

    @Override
    public void doRule(int row, int col, int value, int view) {
        if (!modeSelected) {
            // 正在模式选择，别动棋盘
            return;
        }
        KaLaDeSide side = (KaLaDeSide) sideMap.get(value);
        boolean allSet = true;
        for (Side s : sideMap.values()) {
            if (s.settingUpBoard) {
                allSet = false;
                break;
            }
        }
        if (side.settingUpBoard) {
            // 布局阶段
            side.selectOrSwap(this, row, col);
            noticeAllRefreshGameInfo();//通知界面刷新
            return;
        }
        else if (!allSet) {
            // 等待其他人布局
            return;
        }
        // 正式对局
        // 走棋
        side.selectOrMove(this, row, col);
        if (overFlag == 2) {
            // 已经获胜了，不用处理下面的逻辑了
            return;
        }
        if (cur == value) {
            // 走棋没成功，不用处理下面的逻辑了
            return;
        }
        // 判断对方无棋可走，闷杀
        KaLaDeSide enemy = (KaLaDeSide) sideMap.get(3 - value);
        if (enemy.isNoMove(this)) {
            doOver(side.mSide, enemy.sideString() + "无棋可动，判负");
            return;
        }
        // 判断将军
        boolean checkmate = side.isCheckmate(this);
        // 记录局面
        historyInfo.add(new Pair<>(info(), checkmate));
        while (historyInfo.size() > 9) {
            historyInfo.remove(0);
        }
        // 判断特殊规则和棋或输棋
        /*
          1，单方面长将判定
          甲走棋，形成局面1
          乙走棋，形成局面2
          …
          甲走棋，形成局面7
          乙走棋，形成局面8
          甲走棋，形成局面9
          以上连续9步（4.5回合）中，当1=5=9且2=6且3=7且4=8时开始判定
          （定义将军：当前局面下对方的帅落在本方任何一子的可吃范围）
          同时满足以下所有条件的，为单方面长将，判甲负乙胜：
          （1）1/5/9、3/7局面下甲都在对乙将军
          （2）2/4、6/8局面下乙不都在对甲将军（“不都在”，不是“都不在”）
          其余情形不操作，对局继续
         */
        if (historyInfo.size() == 9) {
            if (historyInfo.get(0).first.equals(historyInfo.get(4).first)
                    && historyInfo.get(0).first.equals(historyInfo.get(8).first)
                    && historyInfo.get(1).first.equals(historyInfo.get(5).first)
                    && historyInfo.get(2).first.equals(historyInfo.get(6).first)
                    && historyInfo.get(3).first.equals(historyInfo.get(7).first)) {
                if (historyInfo.get(0).second && historyInfo.get(2).second
                        && historyInfo.get(4).second && historyInfo.get(6).second && historyInfo.get(8).second &&
                        (!historyInfo.get(1).second || !historyInfo.get(3).second ||
                                !historyInfo.get(5).second || !historyInfo.get(7).second)) {
                    doOver(side.mSide, side.sideString() + "长将，判负");
                }
                return;
            }
        }
    }


    private static class KaLaDeSide extends Side {
        /**
         * 帅是否显示
         */
        public boolean revealGeneral = false;

        public KaLaDeSide(int side) {
            super(side);
        }

        /**
         * 返回己方帅
         *
         * @return 己方的 帅 棋
         */
        public Chess getGeneral() {
            for (Chess chess : chessList) {
                if (chess.mName.equals("帅")) {
                    return chess;
                }
            }
            return null;
        }

        /**
         * 判断己方是否将军
         *
         * @param room 房间信息
         * @return 是否将军
         */
        public boolean isCheckmate(KaLaDeRoom room) {
            for (Chess chess : chessList) {
                List<Integer> eatRange = chess.eatRange(room);
                for (int index : eatRange) {
                    Chess enemy = room.getChessByIndex(index);
                    if (enemy != null && enemy.mName.equals("帅")) {
                        return true;
                    }
                }
            }
            return false;
        }

        /**
         * 判断己方是否无棋可走
         *
         * @param room 房间信息
         * @return 是否无棋可走
         */
        public boolean isNoMove(KaLaDeRoom room) {
            List<Integer> range;
            for (Chess chess : chessList) {
                range = chess.moveRange(room);
                if (range != null && range.size() > 0) {
                    return false;
                }
                range = chess.eatRange(room);
                if (range != null && range.size() > 0) {
                    return false;
                }
            }
            return true;
        }

        @Override
        public List<Integer> swapRange(AbsMultiViewChessRoom room) {
            if (mSide == 1) {
                return new ArrayList<>(Arrays.asList(room.getIndex(1, 1), room.getIndex(1, 8), room.getIndex(2, 3), room.getIndex(2, 6), room.getIndex(0, 1), room.getIndex(0, 2), room.getIndex(0, 3), room.getIndex(0, 4), room.getIndex(0, 5), room.getIndex(0, 6), room.getIndex(0, 7), room.getIndex(0, 8)));
            }
            else {
                return new ArrayList<>(Arrays.asList(room.getIndex(8, 1), room.getIndex(8, 8), room.getIndex(7, 3), room.getIndex(7, 6), room.getIndex(9, 1), room.getIndex(9, 2), room.getIndex(9, 3), room.getIndex(9, 4), room.getIndex(9, 5), room.getIndex(9, 6), room.getIndex(9, 7), room.getIndex(9, 8)));
            }
        }

        @Override
        public boolean canDoEat(AbsMultiViewChessRoom room) {
            return room.getStep() >= 1;
        }

        @Override
        public void startSettingUp(AbsMultiViewChessRoom absRoom, Player player0) {
            KaLaDeRoom room = (KaLaDeRoom) absRoom;
            this.settingUpBoard = true;
            room.gameTip = "请进行布局，可以随意交换布局范围内的棋子";
            player0.registerCustomBottom(
                    new CustomBottom(room, "确定帅位", player -> {
                        if (room.player_black == room.player_white){
                            ((KaLaDeSide) room.sideMap.get(1)).revealGeneral = true;
                            ((KaLaDeSide) room.sideMap.get(2)).revealGeneral = true;
                            room.noticeAllRefreshGameInfo();
                            player.removeCustomBottom(player.findCustomBottomByTitle(room, "确定帅位"));
                            player.dispatchCustomBottom();
                        }
                        else {
                            if ((player == room.player_white) || (((KaLaDeSide) room.sideMap.get(2)).revealGeneral)) {
                                this.revealGeneral = true;
                                room.noticeAllMove();
                                player.removeCustomBottom(player.findCustomBottomByTitle(room, "确定帅位"));
                                player.dispatchCustomBottom();
                            }
                            else {
                                room.gameTip = "必须等白方先确定帅位";
                                room.noticeAllRefreshGameInfo();
                            }
                        }
                    }),
                    new CustomBottom(room, "完成布局", player -> {
                        if (room.player_black == room.player_white){
                            ((KaLaDeSide) room.sideMap.get(1)).revealGeneral = true;
                            ((KaLaDeSide) room.sideMap.get(2)).revealGeneral = true;
                            finishSettingUp(room, player0);
                            player0.clearCustomBottom();
                            player0.dispatchCustomBottom();
                        }
                        else {
                            if (((KaLaDeSide) room.sideMap.get(1)).revealGeneral && ((KaLaDeSide) room.sideMap.get(2)).revealGeneral) {
                                this.revealGeneral = true;
                                finishSettingUp(room, player0);
                                player0.clearCustomBottom();
                                player0.dispatchCustomBottom();
                            }
                            else {
                                room.gameTip = "必须等双方都确定帅位";
                            }
                            room.noticeAllRefreshGameInfo();
                        }
                    })
            );
            room.noticeAllMove();
        }

        public void selectOrSwap(AbsMultiViewChessRoom room, int row, int col) {
            int index = room.getIndex(row, col);
            List<Integer> swapRange = this.swapRange(room);
            if (swapRange == null || !swapRange.contains(index)) {
                return;
            }
            Chess target = getChessByPos(row, col);
            if (target != null && target.mName.equals("帅") && this.revealGeneral) {
                ((KaLaDeRoom) room).gameTip = "公开帅位后不能，更改帅的位置";
                return;
            }
            if (selectedTarget == null) {
                selectedTarget = target;
                return;
            }
            int tempCol = selectedTarget.pCol;
            int tempRow = selectedTarget.pRow;
            selectedTarget.pRow = row;
            selectedTarget.pCol = col;
            if (target != null) {
                target.pRow = tempRow;
                target.pCol = tempCol;
            }
            selectedTarget = null;
        }

        @Override
        public void render(AbsMultiViewChessRoom room, int view) {
            super.render(room, view);
            if ((view & mSide) == 0 && !room.getAllSideSet() && this.revealGeneral) {
                Chess chess = this.getGeneral();
                if (chess != null) {
                    chess.render(room, view);
                }
            }
        }
    }

    private static abstract class KaLaDeChess extends Chess {
        public int moveCount = 0;
        public int eatCount = 0;

        public KaLaDeChess(String name, int mSide, int pRow, int pCol) {
            super(name, mSide, pRow, pCol);
        }

        protected int getDirection() {
            if (mSide == 1) {
                return 1;
            }
            else if (mSide == 2) {
                return -1;
            }
            else {
                return 0;
            }
        }

        protected boolean inSelfBoard() {
            if (mSide == 1) {
                return pRow <= 3;
            }
            else {
                return pRow >= 6;
            }
        }

        protected boolean inEnemyBoard() {
            if (mSide == 1) {
                return pRow >= 6;
            }
            else {
                return pRow <= 3;
            }
        }

        @Override
        public int checkWinAfterMove(AbsMultiViewChessRoom room) {
            moveCount++;
            Chess general = ((KaLaDeSide) room.sideMap.get(mSide)).getGeneral();
            if (general == null) {
                return -1;
            }
            // 帅照面被杀
            List<Integer> faceRange = general.genEatRange(room, new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}, Integer.MAX_VALUE);
            for (int index : faceRange) {
                Chess chess = room.getChessByIndex(index);
                if (chess.mName.equals("帅")) {
                    return -1;
                }
            }
            return 0;
        }

        @Override
        public int checkWinAfterEat(AbsMultiViewChessRoom room, Chess chessAte) {
            eatCount++;
            return chessAte.mName.equals("帅") ? 1 : 0;
        }
    }


    private static class Pawn extends KaLaDeChess {
        public Pawn(int mSide, int pRow, int pCol) {
            super("兵", mSide, pRow, pCol);
        }

        @Override
        public List<Integer> moveRange(AbsMultiViewChessRoom room) {
            if (inSelfBoard()) {
                return genMoveRange(room, new int[][]{{getDirection(), 0}, {getDirection(), -1}, {getDirection(), 1}}, 1);
            }
            else if (inEnemyBoard()) {
                return genMoveRange(room, new int[][]{{1, 0}, {-1, 0}, {getDirection(), -1}, {getDirection(), 1}}, 1);
            }
            else {
                return genMoveRange(room, new int[][]{{getDirection(), 0}}, 1);
            }
        }

        @Override
        public List<Integer> eatRange(AbsMultiViewChessRoom room) {
            if (inSelfBoard()) {
                return genEatRange(room, new int[][]{{getDirection(), 0}}, 1);
            }
            else if (inEnemyBoard()) {
                return genEatRange(room, new int[][]{{1, 0}, {-1, 0}, {getDirection(), -1}, {getDirection(), 1}}, 1);
            }
            else {
                return genEatRange(room, new int[][]{{getDirection(), 0}}, 1);
            }
        }
    }

    private static class General extends KaLaDeChess {
        public General(int mSide, int pRow, int pCol) {
            super("帅", mSide, pRow, pCol);
        }

        @Override
        public List<Integer> moveRange(AbsMultiViewChessRoom room) {
            List<Integer> moveRange = genMoveRange(room, new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}, 1);
            if (mSide == 1) {
                moveRange.removeIf(e -> e >= room.getIndex(6, 0));
            }
            else {
                moveRange.removeIf(e -> e <= room.getIndex(3, 9));
            }
            return moveRange;
        }

        @Override
        public List<Integer> eatRange(AbsMultiViewChessRoom room) {
            List<Integer> eatRange = genEatRange(room, new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}, 1);
            if (mSide == 1) {
                eatRange.removeIf(e -> e >= room.getIndex(6, 0));
            }
            else {
                eatRange.removeIf(e -> e <= room.getIndex(3, 9));
            }
            return eatRange;
        }

        @Override
        public int checkWinAfterMove(AbsMultiViewChessRoom room) {
            // 贴近对方帅被杀
            Chess enemy = ((KaLaDeSide) room.sideMap.get(3 - mSide)).getGeneral();
            if (enemy != null && enemy.pRow - pRow >= -2 && enemy.pRow - pRow <= 2 && enemy.pCol - pCol >= -2 && enemy.pCol - pCol <= 2) {
                return -1;
            }
            return super.checkWinAfterMove(room);
        }
    }

    private static class Dragon extends KaLaDeChess {
        public Dragon(int mSide, int pRow, int pCol) {
            super("龙", mSide, pRow, pCol);
        }

        @Override
        public List<Integer> moveRange(AbsMultiViewChessRoom room) {
            List<Integer> moveRange = new ArrayList<>();
            moveRange.addAll(genMoveRange(room, new int[][]{{1, 1}, {-1, 1}, {-1, -1}, {1, -1}}, 1));
            moveRange.addAll(genMoveRange(room, new int[][]{{-getDirection(), 0}, {0, 1}, {0, -1}}, Integer.MAX_VALUE));
            moveRange.addAll(genMoveRange(room, new int[][]{{getDirection(), 0}}, Integer.MAX_VALUE, 1, 0));
            return moveRange;
        }

        @Override
        public List<Integer> eatRange(AbsMultiViewChessRoom room) {
            List<Integer> eatRange = new ArrayList<>();
            eatRange.addAll(genEatRange(room, new int[][]{{1, 1}, {-1, 1}, {-1, -1}, {1, -1}}, 1));
            eatRange.addAll(genEatRange(room, new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}, 2));
            return eatRange;
        }
    }

    private static class Chariot extends KaLaDeChess {
        public Chariot(int mSide, int pRow, int pCol) {
            super("车", mSide, pRow, pCol);
        }

        @Override
        public List<Integer> moveRange(AbsMultiViewChessRoom room) {
            return genMoveRange(room, new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}, Integer.MAX_VALUE);
        }

        @Override
        public List<Integer> eatRange(AbsMultiViewChessRoom room) {
            return genEatRange(room, new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}, Integer.MAX_VALUE);
        }
    }

    private static class Horse extends KaLaDeChess {
        public Horse(int mSide, int pRow, int pCol) {
            super("马", mSide, pRow, pCol);
        }

        @Override
        public List<Integer> moveRange(AbsMultiViewChessRoom room) {
            return genMoveRange(room, new int[][]{{1, 2}, {1, -2}, {-1, 2}, {-1, -2}, {2, 1}, {2, -1}, {-2, 1}, {-2, -1}}, 1);
        }

        @Override
        public List<Integer> eatRange(AbsMultiViewChessRoom room) {
            return genEatRange(room, new int[][]{{1, 2}, {1, -2}, {-1, 2}, {-1, -2}, {2, 1}, {2, -1}, {-2, 1}, {-2, -1}}, 1);
        }
    }

    private static class Cannon extends KaLaDeChess {
        public Cannon(int mSide, int pRow, int pCol) {
            super("炮", mSide, pRow, pCol);
        }

        @Override
        public List<Integer> moveRange(AbsMultiViewChessRoom room) {
            return genMoveRange(room, new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}, Integer.MAX_VALUE);
        }

        @Override
        public List<Integer> eatRange(AbsMultiViewChessRoom room) {
            List<Integer> eatRange = genEatRange(room, new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}, Integer.MAX_VALUE, 1, 1);
            eatRange.addAll(genEatRange(room, new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}, 2));
            eatRange.removeAll(genEatRange(room, new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}, 1));
            return eatRange;
        }
    }

    private static class Bishop extends KaLaDeChess {
        public Bishop(int mSide, int pRow, int pCol) {
            super("象", mSide, pRow, pCol);
        }

        @Override
        public List<Integer> moveRange(AbsMultiViewChessRoom room) {
            List<Integer> moveRange = genMoveRange(room, new int[][]{{1, 1}, {-1, 1}, {-1, -1}, {1, -1}}, Integer.MAX_VALUE);
            moveRange.removeAll(genMoveRange(room, new int[][]{{1, 1}, {-1, 1}, {-1, -1}, {1, -1}}, 1));
            return moveRange;
        }

        @Override
        public List<Integer> eatRange(AbsMultiViewChessRoom room) {
            List<Integer> eatRange = genEatRange(room, new int[][]{{1, 1}, {-1, 1}, {-1, -1}, {1, -1}}, Integer.MAX_VALUE);
            eatRange.removeAll(genEatRange(room, new int[][]{{1, 1}, {-1, 1}, {-1, -1}, {1, -1}}, 1));
            return eatRange;
        }
    }

    private static class Guard extends KaLaDeChess {
        public Guard(int mSide, int pRow, int pCol) {
            super("士", mSide, pRow, pCol);
        }

        @Override
        public List<Integer> moveRange(AbsMultiViewChessRoom room) {
            return genMoveRange(room, new int[][]{{1, 1}, {-1, 1}, {-1, -1}, {1, -1}}, 1);
        }

        @Override
        public List<Integer> eatRange(AbsMultiViewChessRoom room) {
            return genEatRange(room, new int[][]{{1, 1}, {-1, 1}, {-1, -1}, {1, -1}}, 1);
        }
    }
}
